<?php

namespace app\bll;

use app\lib\installer\General as LibInstallerGeneral;
use support\Db;
use Exception;
use hulang\tool\FileHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Melihovv\Base64ImageDecoder\Base64ImageEncoder;


/**
 * 元素组件
 */
class ComponentElement
{
    /**
     * 查找一个元素
     *
     * @param integer $id
     * @return array
     */
    public function find(int $id): array
    {
        $data =  Db::table('frame_component_element')
            ->where('id', '=', $id)
            ->first();
        return $data === null ? [] : get_object_vars($data);
    }

    /**
     * 查找所有元素
     */
    public function select()
    {
        return Db::table('frame_component_element')
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }



    /**
     * 手动安装
     *
     * @return void
     */
    public  function install()
    {

        $libInstallerGeneral = new LibInstallerGeneral();
        $installres =  $libInstallerGeneral->install(
            get_element_path(),
            ['describe.php']
        );


        try {
            //读写配置文件写入数据库
            $describe          = $this->getDescribe($installres['foldername']);
            Db::table('frame_component_element')
                ->insertGetId($describe);
        } catch (Exception $ex) {
            throw $ex;
        }
    }


    /**
     * 在线升级
     *
     * @return void
     * @throws Exception
     */
    public function onlineUpgrade(int $id)
    {
        $element = $this->find($id);

        if (! $element) {
            throw new Exception('元素不存在');
        }

        if (! $element['upgrade']) {
            throw new Exception('元素没有升级地址');
        }

        try {
            $upgrade = $element['upgrade'];

            $libInstallerGeneral = new LibInstallerGeneral();
            $res = $libInstallerGeneral->onlineUpgrade(
                $upgrade,
                $element['version'],
                get_element_path(),
                ['describe.php']
            );

            $describe          = $this->getDescribe($element['ident']);
            $describe['nextversion'] = '';
            $describe['version'] = $res['nextVersion'];

            Db::table('frame_component_element')
                ->where('id', '=', $id)
                ->update($describe);
        } catch (Exception $ex) {
            throw $ex;
        }
    }

    /**
     * 手动上传升级
     *
     * @return void
     */
    public function handleUpgrade(int $id)
    {
        try {
            $element = $this->find($id);

            $libInstallerGeneral = new LibInstallerGeneral();
            $res = $libInstallerGeneral->offlineUpgrade(
                $element['ident'],
                $element['version'],
                get_element_path(),
                ['describe.php']
            );

            $describe          = $this->getDescribe($element['ident']);
            $describe['nextversion'] = '';
            $describe['version'] = $res['nextVersion'];

            Db::table('frame_component_element')
                ->where('id', '=', $id)
                ->update($describe);
        } catch (Exception $ex) {
            throw $ex;
        }
    }

    /**
     * 卸载
     *
     * @param integer $id
     * @throws Exception
     * @return void
     */
    public function uninstall(int $id)
    {
        try {
            $element = $this->find($id);
            //数据删除 
            Db::table('frame_component_element')
                ->where('id', '=', $id)
                ->delete();
            //文件夹删除
            $dir = get_element_path() . $element['ident'] . '/';
            if (file_exists($dir)) {
                FileHelper::delDir($dir);
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }



    /**
     * 获取插件的配置文件
     *
     * @param string $elemetfoldername 元素文件夹名称
     * @return array
     */
    public function getDescribe(string $elemetfoldername): array
    {
        $describe          = include get_element_path() . $elemetfoldername . '/describe.php';
        $describe['ident'] = $elemetfoldername;
        $logo              = get_element_path() . $elemetfoldername . '/logo.png';

        if (file_exists($logo)) {
            $imageEncoder = Base64ImageEncoder::fromFileName(
                $logo,
                ['jpeg', 'png', 'gif']
            );

            $describe['logo'] =  $imageEncoder->getDataUri();
        }
        $describe['source']      = isset($describe['source']) ? $describe['source'] : 0;
        $polymorphic             = isset($describe['polymorphic']) ? $describe['polymorphic'] : [];
        $describe['polymorphic'] = json_encode($polymorphic, JSON_UNESCAPED_UNICODE);
        return $describe;
    }

    /**
     * 元素层级显示
     *
     * @param boolean $formatjson
     * @return array|string
     */
    public function cascader(string $field = '*', bool $formatjson = true): array|string
    {
        $elements = Db::table('frame_component_element')
            ->get(['id', 'ident', 'title', 'pid', 'polymorphic', 'source'])
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
        foreach ($elements as $key => $element) {
            if ($element['polymorphic']) {
                $polymorphics = json_decode($element['polymorphic'], true);
                if ($polymorphics) {
                    $children = [];
                    foreach ($polymorphics as $k => $polymorphic) {
                        $children[] = [
                            'id'    => $element['id'] . '_' . $k,
                            'ident' => $element['ident'] . '_' . $k,
                            'name'  =>  $polymorphic,
                            'title' =>  $polymorphic,
                            'source' => $element['source']
                        ];
                    }
                    $elements[$key]['children'] = $children;
                }
            }

            unset($elements[$key]['polymorphic']);
        }

        return $formatjson ? json_encode($elements, JSON_UNESCAPED_UNICODE)
            : $element;
    }


    /**
     * 检查更新
     *
     * @param integer|array $id 元素id OR 元素数组
     * @return boolean
     */
    public function checkUpgrade(int|array $elem_or_id): bool
    {
        $element = $elem_or_id;
        if (is_int($elem_or_id)) {
            $element = $this->find($elem_or_id);
        }
        $upgrade = $element['upgrade'];
        if ($upgrade == '') {
            return false;
        }

        $libInstallerGeneral = new LibInstallerGeneral();
        $nextVersion = $libInstallerGeneral->getNextUpgradeVersion($upgrade, $element['version']);

        if ($nextVersion) {
            Db::table('frame_component_element')
                ->where('id', '=', $element['id'])
                ->update([
                    'nextversion' => $nextVersion['nextVersion']
                ]);
            return true;
        }
        Db::table('frame_component_element')
            ->where('id', '=', $element['id'])
            ->update([
                'nextversion' => ''
            ]);
        return false;
    }

    /**
     * 检查所有元素是否有版本更新
     *
     * @return void
     */
    public function checkAllUpgrade()
    {
        $elements = $this->select();
        foreach ($elements as $element) {
            $this->checkUpgrade($element);
        }
    }




    /**
     * 分页查询
     *
     * @param string $name
     * @param string $ident
     * @param integer $pagesize
     * @return LengthAwarePaginator
     */
    public function paginator(
        string $ident = '',
        string $title = '',
        int $pagesize = 20
    ): LengthAwarePaginator {
        return  Db::table('frame_component_element')
            ->when($ident != '', function ($query) use ($ident) {
                $query->where('ident', 'like', '%' . $ident . '%');
            })
            ->when($title != '', function ($query) use ($title) {
                $query->where('title', 'like', '%' . $title . '%');
            })

            ->paginate($pagesize);
    }
}
